import { Body, Controller, Delete, Get, HttpException, HttpStatus, Inject, Param, Patch, Post, Put, Query, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Authorities } from 'src/decorators/authorities.decorator';
import { AuthoritiesGuard } from 'src/guards/authorities.guard';
import { UuidResDto } from 'src/modules/common/dtos/uuid.res.dto';
import { Guard } from 'src/modules/guard/constants/guard.constant';
import { RoleService } from 'src/modules/role/services/role.service';
import { CreateUserAdminReqDto } from 'src/modules/user/dtos/user/admin/create-user.admin.req.dto';
import { GetUserListAdminReqDto } from 'src/modules/user/dtos/user/admin/get-user-list.admin.req.dto';
import { GetUserPagedListAdminResDto } from 'src/modules/user/dtos/user/admin/get-user-paged-list.admin.res.dto';
import { RequestContext } from '../../../../context/request.context';
import { LantUtil } from '../../../../utils/lant.util';
import { BoolResDto } from '../../../common/dtos/bool.res.dto';
import { CountResDto } from '../../../common/dtos/count.res.dto';
import { IdReqDto } from '../../../common/dtos/id.req.dto';
import { UuidReqDto } from '../../../common/dtos/uuid.req.dto';
import { ConfigProvider } from '../../../config/constants/config.constant';
import { ConfigService } from '../../../config/services/config.service';
import { LogAppender } from '../../../log/constants/log.constant';
import { LogService } from '../../../log/services/log.service';
import { OrganizationService } from '../../../organization/services/organization.service';
import { UserType } from '../../../role/constants/role.constant';
import { ChangePasswordAdminReqDto } from '../../dtos/user/admin/change-password.admin.req.dto';
import { ChangeRoleListAdminReqDto } from '../../dtos/user/admin/change-role-list.admin.req.dto';
import { LoginAdminReqDto } from '../../dtos/user/admin/login.admin.req.dto';
import { LoginAdminResDto } from '../../dtos/user/admin/login.admin.res.dto';
import { LoginValidateAdminReqDto } from '../../dtos/user/admin/login.validate.admin.req.dto';
import { ResetPasswordAdminReqDto } from '../../dtos/user/admin/reset-password.admin.req.dto';
import { UpdateOrganizationListAdminReqDto } from '../../dtos/user/admin/update-organization-list.admin.req.dto';
import { UpdateUserRoleListAdminReqDto } from '../../dtos/user/admin/update-user-role-list.admin.req.dto';
import { UpdateUserAdminReqDto } from '../../dtos/user/admin/update-user.admin.req.dto';
import { AuthService } from '../../services/auth.service';
import { UserService } from '../../services/user.service';
import { UserUtil } from '../../utils/user.util';
const Geetest = require('geetest');

@ApiBearerAuth()
@ApiTags('users')
@Controller('admin/users')
export class UserAdminController {
  constructor(
    @Inject(ConfigProvider)
    private readonly configService: ConfigService,
    private readonly userService: UserService,
    private readonly organizationService: OrganizationService,
    private readonly roleService: RoleService,
    private readonly authService: AuthService,
    private readonly logService: LogService,
    private readonly userUtil: UserUtil,
    private readonly lantUtil: LantUtil
  ) {}

  /**
   * 用户登录方法
   * @param identifier 用户名
   * @param password 密码
   */
  async tryLogin(identifier: string, password: string): Promise<LoginAdminResDto> {
    const user = await this.userService.login(identifier, password);
    if (!user) throw new HttpException('用户名或密码错误', HttpStatus.BAD_REQUEST);

    const { roleList, guardList } = await this.userUtil.getRoleAndGuardList(user);
    const { expiresIn, accessToken } = await this.authService.createToken(user.uuid, user.jobId, guardList, roleList);

    return {
      token: accessToken,
      expires_in: expiresIn,
      guard_list: Object.values(guardList),
      job_id: user.jobId,
      organization_list: user.organizationList?.map(organization => {
        return {
          id: organization.id,
          name: organization.name,
          level: organization.level
        };
      })
    };
  }

  @ApiOperation({ summary: '后台管理员登录' })
  @ApiOkResponse({ description: '用户token信息', type: LoginAdminResDto })
  @Post('login')
  async login(@Body() { identifier, password }: LoginAdminReqDto): Promise<LoginAdminResDto> {
    try {
      return this.tryLogin(identifier, password);
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot login by ${[identifier, password]}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '后台管理员登录（Geetest）' })
  @ApiOkResponse({ description: '用户token信息', type: LoginAdminResDto })
  @Post('validate')
  async validate(@Body() body: LoginValidateAdminReqDto): Promise<LoginAdminResDto> {
    try {
      const captcha = new Geetest({
        geetest_id: this.configService.geetest.id,
        geetest_key: this.configService.geetest.key
      });

      const result = await captcha.validate({
        challenge: body.geetest_challenge,
        validate: body.geetest_validate,
        seccode: body.geetest_seccode
      });

      if (result) {
        return this.tryLogin(body.identifier, body.password);
      }

      throw new HttpException('登录校验失败，请重试', HttpStatus.BAD_REQUEST);
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot login by ${JSON.stringify(body)}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '管理员创建用户' })
  @ApiOkResponse({ description: '用户uuid', type: UuidResDto })
  @Authorities(Guard.UserManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Post()
  async create(@Body() body: CreateUserAdminReqDto): Promise<UuidResDto> {
    try {
      const count = await this.userService.checkDuplicateByUsername(body.username);
      if (count > 0) throw new HttpException(`${body.username}已存在，不能重复创建`, HttpStatus.BAD_REQUEST);

      const user = await this.userService.create(
        body.username,
        body.password,
        body.role_uuid_list,
        body.organization_id_list,
        body.job_id,
        UserType.user,
        body.email,
        body.mobile,
        body.active,
        body.display_name
      );

      return { uuid: user?.uuid };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot create by ${JSON.stringify(body)}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '管理员移除账户角色' })
  @ApiOkResponse({ description: '返回修改结果', type: BoolResDto })
  @Authorities(Guard.UserManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Delete('roles')
  async deleteUserRoleList(@Body() { user_uuid, role_uuid }: ChangeRoleListAdminReqDto): Promise<BoolResDto> {
    try {
      const user = await this.userService.findOneByUuid(user_uuid);
      if (!user || user.deleted) throw new HttpException('当前用户不存在或已被删除', HttpStatus.BAD_REQUEST);
      if (user.type == UserType.system) throw new HttpException('不能移除系统用户', HttpStatus.BAD_REQUEST);

      const userRole = await this.roleService.findOneByUuid(role_uuid);
      if (!userRole) throw new HttpException('该角色不存在或已被删除', HttpStatus.BAD_REQUEST);

      const roleUuidList = user.roleUuidList.filter(uuid => uuid != role_uuid);
      const updateResult = await this.userService.changeRoleList(user.uuid, roleUuidList);

      const guardList = await this.userUtil.getGuardList(user);
      await this.authService.updateToken(user.uuid, user.jobId, guardList);

      return { result: !!updateResult.affected };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot change role by ${[user_uuid, role_uuid]}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '删除用户' })
  @ApiOkResponse({ description: '返回删除结果', type: BoolResDto })
  @Authorities(Guard.UserManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Delete(':uuid')
  async delete(@Param() { uuid }: UuidReqDto): Promise<BoolResDto> {
    try {
      const user = await this.userService.findOneByUuid(uuid);
      if (!user || user.deleted) throw new HttpException('该用户不存在或已经被删除', HttpStatus.BAD_REQUEST);
      if (user.type == UserType.system) throw new HttpException('不能删除系统用户', HttpStatus.BAD_REQUEST);

      const updateResult = await this.userService.deleteByUuid(uuid);

      return { result: !!updateResult.affected };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot delete user by ${uuid}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '修改用户登录密码' })
  @ApiOkResponse({ description: '返回修改结果', type: BoolResDto })
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Patch('password/change')
  async changePassword(@Body() { old_password, new_password }: ChangePasswordAdminReqDto): Promise<BoolResDto> {
    try {
      const currentUser = RequestContext.currentUser();

      const user = await this.userService.findOneByUuid(currentUser.uuid);
      if (!user || user.deleted) throw new HttpException('当前用户不存在或已被删除', HttpStatus.BAD_REQUEST);

      const userByPassword = await this.userService.findOneByUuidAndPassword(currentUser.uuid, old_password);
      if (!userByPassword) throw new HttpException('原密码不正确', HttpStatus.BAD_REQUEST);

      const updateResult = await this.userService.changePassword(currentUser.uuid, new_password);

      return { result: !!updateResult.affected };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot change password, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '重置用户登录密码' })
  @ApiOkResponse({ description: '返回修改结果', type: BoolResDto })
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Patch('password/reset')
  async resetPassword(@Query() query: ResetPasswordAdminReqDto): Promise<BoolResDto> {
    try {
      const user = await this.userService.findOneByUuid(query.user_uuid);
      if (!user || user.deleted) throw new HttpException('当前用户不存在或已被删除', HttpStatus.BAD_REQUEST);

      const updateResult = await this.userService.changePassword(query.user_uuid, query.password);

      return { result: !!updateResult.affected };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot reset password, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '更新用户信息' })
  @ApiOkResponse({ description: '返回用户更新结果', type: BoolResDto })
  @Authorities(Guard.UserManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Put(':uuid')
  async updateUser(@Param() { uuid }: UuidReqDto, @Body() body: UpdateUserAdminReqDto): Promise<BoolResDto> {
    try {
      if (body.username) {
        const count = await this.userService.checkDuplicateByUsername(body.username, uuid);
        if (count > 0) throw new HttpException(`${body.username}已存在，不能重复创建`, HttpStatus.BAD_REQUEST);
      }

      const updateResult = await this.userService.updateUser(uuid, body.username, undefined, body.job_id, body.organization_id_list, body.email, body.mobile, body.active, body.display_name);

      return { result: !!updateResult };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot update by ${JSON.stringify(body)}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '更新用户组织信息' })
  @ApiOkResponse({ description: '返回用户组织更新结果', type: BoolResDto })
  @Authorities(Guard.UserManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Patch(':uuid/organizations')
  async updateOrganizations(@Param() { uuid }: UuidReqDto, @Body() body: UpdateOrganizationListAdminReqDto): Promise<BoolResDto> {
    try {
      const organizationList = await this.organizationService.findByIdList(body.organization_id_list);
      if (!organizationList || organizationList.length <= 0) throw new HttpException('组织列表不存在或已被删除', HttpStatus.BAD_REQUEST);
      const organizationIdList = organizationList.map(organization => organization.id);
      const updateResult = await this.userService.updateOrganizationList(uuid, organizationIdList);

      return { result: !!updateResult };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot update by ${JSON.stringify(body)}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '更新用户岗位信息' })
  @ApiOkResponse({ description: '返回岗位更新结果', type: BoolResDto })
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Patch('jobs/:id')
  async updateJob(@Param() { id }: IdReqDto): Promise<BoolResDto> {
    try {
      const user = RequestContext.currentUser();
      const userInfo = await this.userService.findOneByUuid(user.uuid);
      if (!userInfo.organizationIdList.includes(id)) throw new HttpException('岗位编号不合法', HttpStatus.BAD_REQUEST);

      const updateResult = await this.userService.updateJob(user.uuid, id);
      await this.authService.updateToken(user.uuid, id);

      return { result: !!updateResult };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot update by ${id}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '批量更新用户角色' })
  @ApiOkResponse({ description: '返回批量更新用户角色成功数量', type: CountResDto })
  @Authorities(Guard.UserManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Patch('roles/:uuid')
  async updateUserRoleList(@Param() { uuid }: UuidReqDto, @Body() body: UpdateUserRoleListAdminReqDto): Promise<CountResDto> {
    try {
      const userRole = await this.roleService.findOneByUuid(uuid);
      if (!userRole) throw new HttpException('当前用户角色不存在或已被删除', HttpStatus.BAD_REQUEST);
      if (!body.user_uuid_list || body.user_uuid_list.length <= 0) throw new HttpException('用户列表信息不合法', HttpStatus.BAD_REQUEST);

      const resultList = [];
      for (const userUuid of body.user_uuid_list) {
        const user = await this.userService.findOneByUuid(userUuid);
        if (user && !user.roleUuidList.includes(uuid)) {
          user.roleUuidList.push(uuid);
          const updateResult = await this.userService.changeRoleList(userUuid, user.roleUuidList);
          if (!!updateResult.affected) {
            const guardList = await this.userUtil.getGuardList(user);
            await this.authService.updateToken(user.uuid, user.jobId, guardList);

            resultList.push(true);
          }
        }
      }

      return { count: resultList.length };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Admin user cannot update by ${JSON.stringify(body)}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '获取用户列表' })
  @ApiOkResponse({ description: '返回用户列表信息', type: GetUserPagedListAdminResDto })
  @Authorities(Guard.UserManage, Guard.UserView)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Get()
  async getAll(@Query() query: GetUserListAdminReqDto): Promise<GetUserPagedListAdminResDto> {
    try {
      const currentUser = RequestContext.currentUser();
      const user = await this.userService.findOneByUuid(currentUser.uuid);
      const organizationId = query.organization_id ? query.organization_id : user.jobId;
      const userType = query.organization_id ? UserType.user : user.type;
      const [userList, count] = await this.userService.findAndPaging(
        query.page_index,
        query.page_size,
        query.sort_type,
        query.order_by,
        userType,
        user.uuid,
        organizationId,
        query.name,
        query.email,
        query.mobile,
        query.role_uuid,
        query.include_role
      );

      return {
        total_count: count,
        data: userList?.map(user => {
          return {
            uuid: user.uuid,
            type: user.type,
            username: user.username,
            display_name: user.displayName,
            avatar_url: user.avatarUrl,
            email: user.email,
            mobile: user.mobile,
            active: user.active,
            job_id: user.jobId,
            organization_id_list: user.organizationIdList,
            created_at: user.createdAt
          };
        })
      };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.fatal(LogAppender.user, `Cannot get user list by ${JSON.stringify(query)}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
}
